﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace Clandestine.Entities
{
    public enum MoveSpeed
    {
        Idle,
        Walk,
        Run,
        Teleport
    }

    public abstract class NPC : Entity
    {
        public bool DisableAI = false;
        public AnimatedSprite AnimatedSprite = new AnimatedSprite();

        public override Sprite Sprite
        {
            get { return (Sprite)AnimatedSprite; }
        }

        // Speed is in terms of the number of pixels moved per millisecond.
        public float WalkSpeed { get; set; }
        private const float runSpeed = 0f;

        public bool IsMoving
        {
            get { return !(moveDstCoord == Position); }
        }

        private string spriteMetaData = "sprite_metadata/npc.xml";
        public string SpriteMetaData
        {
            get { return spriteMetaData; }
            set 
            { 
                spriteMetaData = value; 
                InitializeAnimations();  
            }
        }

        protected Point moveDstCoord = new Point();
        private float moveDstSpeed = 0f;
        private MoveSpeed ms = MoveSpeed.Idle;
        private Direction direction = Direction.Up;
        private PointF floatPosition = new PointF();
        private bool highPriorityDst = false;
        protected bool hasCollided = false; // whether we've collided with something in trying to reach our goal.

        private CompletedEvent moveCompletionEvent = null;

        private object movementLockBlob = new object();

        private TickCallback thinkTicker;

        internal abstract void think();

        private string spritesheet = string.Empty;
        public virtual string Spritesheet
        {
            get { return spritesheet; }
            set
            {
                spritesheet = value;
                InitializeAnimations();
            }
        }

        public Direction Direction
        {
            get { return direction; }
            set
            {
                direction = value;
                if ((AnimatedSprite != null) && (AnimatedSprite.CurrentAnimation != null))
                    SetAnimation();
            }
        }

        public MoveSpeed MoveSpeed
        {
            get { return ms; }
            set
            {
                ms = value;
                if ((AnimatedSprite != null) && (AnimatedSprite.CurrentAnimation != null))
                    SetAnimation();
            }
        }

        public bool Moving
        {
            get
            {
                return (moveDstCoord == floatPosition);
            }
        }

        public override bool Solid
        {
            get
            {
                return true;
            }
        }

        public virtual void InitializeAnimations()
        {
            if ((spritesheet == string.Empty) || (spriteMetaData == null))
                return;
            AnimatedSprite.ClearAnimations();
            AnimatedSprite.AddAnimations(spritesheet, SpriteMetaData);
        }

        protected virtual void tick(float deltaMs)
        {
            if (!WorldMap.Paused && !DisableAI)
                think();

            if (!WorldMap.Paused || highPriorityDst)
                movementStep(deltaMs);

            if (WorldMap.Paused && !highPriorityDst)
                MoveSpeed = MoveSpeed.Idle;
        }

        private void movementStep(float deltaMs)
        {
            lock (movementLockBlob)
            {
                PointF oldPos = this.floatPosition; // incase we collide!

                if (Point.Round(floatPosition) != moveDstCoord)
                {
                    // (Note the order is set out such that if a diagonal movement is taking
                    //  place, the vertical animation will be set rather than the horizontal one.)
                    if (moveDstCoord.X < floatPosition.X) // LEFT!
                    {
                        Direction = Direction.Left;
                        floatPosition.X -= moveDstSpeed * deltaMs;
                        if (floatPosition.X < moveDstCoord.X)
                            floatPosition.X = moveDstCoord.X;
                    }
                    else if (moveDstCoord.X > floatPosition.X) // RIIIGHT!
                    {
                        Direction = Direction.Right;
                        floatPosition.X += moveDstSpeed * deltaMs;
                        if (floatPosition.X > moveDstCoord.X)
                            floatPosition.X = moveDstCoord.X;
                    }
                    else if (moveDstCoord.Y < floatPosition.Y) // Gotta go up!
                    {
                        Direction = Direction.Up;
                        floatPosition.Y -= moveDstSpeed * deltaMs;
                        if (floatPosition.Y < moveDstCoord.Y) // Overshot?
                            floatPosition.Y = moveDstCoord.Y;
                    }
                    else if (moveDstCoord.Y > floatPosition.Y) // Down!
                    {
                        Direction = Direction.Down;
                        floatPosition.Y += moveDstSpeed * deltaMs;
                        if (floatPosition.Y > moveDstCoord.Y)
                            floatPosition.Y = moveDstCoord.Y;
                    }

                    if (Clandestine.Collision.CheckCollision(new Rectangle(Point.Round(floatPosition).X, Point.Round(floatPosition).Y + this.Size.Height - 10, this.Size.Width, 10), (this.Layer == WorldMap.Player.Layer) ? true : false, this.Layer, this))
                    {
                        this.hasCollided = true;
                        this.floatPosition = oldPos;
                    }
                    else
                    {
                        this.Sprite.Position = Point.Round(floatPosition);
                        SetAnimation();

                        // Is this the end?
                        if (floatPosition == moveDstCoord)
                        {
                            MoveSpeed = MoveSpeed.Idle;
                            if (moveCompletionEvent != null)
                                moveCompletionEvent();
                        }
                    }
                }
            }
        }

        public NPC()
        {
            thinkTicker = Ticker.AddTimer(new DeltaMsTimedEvent(tick));
        }

        public void StopMoving()
        {
            lock (movementLockBlob)
            {
                moveDstCoord = Point.Round(floatPosition);
                floatPosition = moveDstCoord;
                MoveSpeed = MoveSpeed.Idle;
            }
        }

        public void MoveTo(bool highPriority, MoveSpeed speed, Point destination, CompletedEvent completionEvent)
        {
            lock (movementLockBlob)
            {
                highPriorityDst = highPriority;
                MoveSpeed = speed;
                switch (speed)
                {
                    case MoveSpeed.Teleport:
                        this.Position = destination;
                        moveDstCoord = destination;
                        if (completionEvent != null)
                            completionEvent();
                        break;

                    case MoveSpeed.Walk:
                        moveDstCoord = destination;
                        moveDstSpeed = WalkSpeed;
                        //MoveSpeed = MoveSpeed.Run;
                        break;

                    case MoveSpeed.Run:
                        moveDstCoord = destination;
                        moveDstSpeed = runSpeed;
                        //MoveSpeed = MoveSpeed.Run;
                        break;
                }

                moveCompletionEvent = completionEvent;
            }
        }

        public virtual void SetAnimation()
        {
            // Set the animation
            string prefix = ((MoveSpeed == MoveSpeed.Walk) || (MoveSpeed == MoveSpeed.Idle)) ? "walk_" : "run_";
            string direction = Direction.ToString().ToLower();
           // string name = prefix + direction;
            if (AnimatedSprite.CurrentAnimation.Name != prefix + direction)
                AnimatedSprite.SetAnimation(prefix + direction);

            // Play?
            if (MoveSpeed == MoveSpeed.Idle)
            {
                AnimatedSprite.Playing = false;
                AnimatedSprite.CurrentFrame = 1; // Idle frame
            }
            else
                AnimatedSprite.Playing = true;
        }

        public override Point Position
        {
            get { return base.Position; }
            set
            {
                lock (movementLockBlob)
                {
                    base.Position = value;
                    floatPosition = new PointF(value.X, value.Y);
                    moveDstCoord = Point.Round(floatPosition);
                }
            }
        }

        public override void Dispose()
        {
            if (Sprite != null)
                Sprite.Dispose();
            Ticker.RemoveTickCallback(thinkTicker);
        }
    }
}
